package paws.core

import org.jgrapht._
import org.jgrapht.graph._
import org.jgrapht.alg._
import paws.core._
import paws.core.PowerLevel._
import paws.core.util.SimulationUtils._
import scala.collection.JavaConversions._



class NodeLink extends DefaultWeightedEdge {
  def _source = getSource.asInstanceOf[Node]
  def _target = getTarget.asInstanceOf[Node]
  def _weight = getWeight
}


// class LinkFactory extends EdgeFactory[Node, NodeLink] {
//   def createEdge(a: Node, b: Node) = NodeLink(a, b)
// }

// This class is used to find routes between nodes
class Router {
  // type NodeLink = DefaultWeightedEdge
  type LinkFactory = ClassBasedEdgeFactory[Node, NodeLink]
  type Network = SimpleDirectedWeightedGraph[Node, NodeLink]
  type PathFinder = DijkstraShortestPath[Node, NodeLink]
  
  private[core] val linkFactory = new LinkFactory(classOf[NodeLink])
  private[core] val network = new Network(linkFactory)
  
  def addNode(n: Node): Unit = 
    synchronized {
      network.addVertex(n)
    }
  
  def removeNode(n: Node): Unit = 
    synchronized {
      network.removeVertex(n)
    }
  
  def addLink(a: Node, b: Node): Unit = 
    synchronized {
      network.addEdge(a, b)
      network.addEdge(b, a)
    }
  
  def addAndLink(a: Node) = 
    synchronized {
      val p = a.getParameters
      network.addVertex(a)
      for(b <- network.vertexSet if b != a) {
        val d = distance(a, b)
        if(d < p.range(High)) {           // if the distance from a to b < max. range
          val ab = 
            linkFactory.createEdge(a, b)  // create bidirectional link between a and b
          val ba = 
            linkFactory.createEdge(b, a)
          network.addEdge(a, b, ab)       // add it to the network graph
          network.addEdge(b, a, ba)
          network.setEdgeWeight(ab, d)    // weight it with the distance between the nodes
          network.setEdgeWeight(ba, d)
        }        
      }        
    }
  
  def getNextHop(a: Node, b: Node) = 
    getPath(a, b) match {
      case x :: rest => Some(x)
      case Nil       => None 
    }
  
  def getPath(a: Node, b: Node): List[Node] =  
    synchronized {
      if(!network.containsVertex(a) || !network.containsVertex(b))
        return Nil
      val pathfinder = new PathFinder(network, a, b)
      val edges = 
        pathfinder.getPathEdgeList match {
          case null => Nil
          case list => list.toList
        }
      edges.map(_._target)
    } 
  
  def getNodes = network.vertexSet.toSet
  
  def getNeighbors(n: Node): Int = network.outDegreeOf(n)
}

object RouterTest {
  def main(args: Array[String]): Unit = {
    import scala.util.Random._
    
    var nodes: List[Node] = Nil
    for(i <- 1 to 100) {
      val x, y = nextInt(10000) / 100.0
      val n = new Node(null, null)
      n.setLocation(x, y)
      n.setID(i)
      nodes = n :: nodes      
    }
    
    val router = new Router
    //for(a <- nodes)
    //  router.addAndLinkIf(a){(x, y) => distance(x,y) < 3}
    
    val a = nodes(nextInt(nodes.length))
    val b = nodes(nextInt(nodes.length))
    val path = a :: router.getPath(a, b)
    
    println(a + " -> " + b)
    path.foreach(println)
  }
  
  def distance(a: Node, b: Node): Double = {
    val (ax, ay) = a.getLocation
    val (bx, by) = b.getLocation
    val dx = ax - bx
    val dy = ay - by
    val distance = Math.sqrt((dx * dx) + (dy * dy))
    distance
  }
}